LambdaでCloudFrontのログをリネームしてみた
はじめに
AWSチームのすずきです。
AWSがCDNとして提供するCloudFront、そのアクセスログは任意のS3バケットに出力する事が可能です。
Amazon CloudFront » 開発者ガイド » アクセスログ
2014年11月のアップデートにより、CloudFrontがS3上に出力するアクセスログのファイル数は大きく減少しましたが、 一定の利用があるサイトでは、アクセスログとして出力されるログファイル数は1日数百に達するため、 ログの蓄積期間が長い場合、S3の特定パスに格納されているファイル数が多くなり、操作性に問題が出る場合がありました。
今回、S3イベントで起動するLambdaを利用し、ログファイルのキーに日付情報を付与するリネーム処理により、ログの操作性を改善する機会がありましたので、その内容について紹介をさせていただきます。
事前準備
- CloudFront, S3のログ保管設定、Lambdaが利用するIAM設定を行います。
CloudFront
- CloudFrontのログ設定、AWSコンソールのCloudFront画面より、確認、変更が可能です。
- 「Logbucket」が指定されていない場合、「Edit」より任意のS3バケットとログの有効化を行います。
- 「Log Prefix」は、CloudFrontのDistribution毎に定義、CNAME、またはDistributionのDNS名とする事をおすすめします。
S3設定
- システムに求められるログ保持要件にあわせ、適切なライフルサイクル設定を行う事をお勧めします。
IAMロール
- Lambdaファンクションで利用するIAMポリシーを反映します
ポリシードキュメント
- LambdaのBluePrint、S3アクセス用のサンプル「lambda_s3_exec_role」を元に、次の修正を実施して利用しました。
-
s3:DeleteObject 権限の追加
- S3操作対象を限定するため、ResourceでS3バケットを指定
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::<ログ用S3バケット名>/*" ] } ] }
設定
Lambda ファンクションの設置
- Lambdaファンクションを設置します
- ログ出力先のS3と同じリージョンにLambdaファンクションを設置します。
- Lambda のサンプルプログラムは指定せず、Skipします。
- 言語はPython 2.7を利用しました
コードの反映
blueprintとして公開されている「s3-get-object-python」を元に、以下の修正を実施したコードを反映します。
- 日付処理のため、「datetime」 をインポート
- 新しいキー名(newkey)に、日付情報を付与
- S3の高レベルAPI「copy_from」を利用し、日付を付与した新しいキーでコピーを実行
- コピー完了後、旧ファイルは消去
from __future__ import print_function import json import urllib import boto3 import datetime print('Loading function') s3 = boto3.resource('s3') def lambda_handler(event, context): # Get the object from the event and show its content type bucket = event['Records'][0]['s3']['bucket']['name'] key = urllib.unquote_plus(event['Records'][0]['s3']['object']['key']).decode('utf8') # Add Prefix datetime newkey = "archive-" + datetime.date.today().isoformat() + "/" + key try: response = s3.Object(bucket, newkey).copy_from(CopySource={'Bucket': bucket, 'Key': key}) # print(response) response = s3.Object(bucket, key).delete() # print(response) except Exception as e: print(e) raise e
- ロールは先に作成したLambda用ロールを指定します
- 設定内容を確認して、ファンクションを作成します。
イベント登録
- 「Event sources」タブより、「Add event source」を実施します。
- Event event source typeとして「S3」
- BucketはCloudFrontのログ出力先を指定します
- EventTypeは「Object Create」より「Put」を選択します
- CloudFrontのログ設定に応じ「Prefix」を指定します
動作確認
S3
- Lambdaイベント設置後、CloudFrontのログは「/archive/(日付)/」に移動します。
Lambda (Monitoring)
- 過去24時間のLambdaの稼働状況が確認できます。
- S3イベント設定変更後、設定不備で無限ループに陥っていない事を確認することをお勧めします。
- より詳細な情報、前日以前のデータはCloudwatch画面にて確認可能です
Invocations
- 1時間に実行されたLambdaファンクションの実行回数が表示されます
- CloudFrontが1時間に出力するログ件数と比較し、大きく乖離している場合Lambdaのイベント設定を確認します
Duration
- 1時間に実行されたLambdaファンクションの実行のべ時間がmsで表示されます。
Errors
- Lambdaファンクションが異常終了した件数が表示されます。
- エラーが継続して発生している場合、該当時間の稼働ログ、後述のCloudwatchLogsで確認することをお勧めします
Throttles
- 初期状態ではLambdaファンクションの同時実行数100に制限されており、その制限に触れた場合に表示されます。
- S3イベントのLambdaは再実行が期待できないため、必要に応じログの手動移動を行います。
- Throttlesが頻繁に発生する場合、上限緩和申請をAWSサポートに行います。
CloudwatchLogs
- Lambdaファンクションの実行ログは、CloudwatchLogsで確認可能です。
- 今回のLambdaファンクション、1回の実行時間は400〜800ms、使用メモリは30MB前後、最小の128MBのメモリ指定で、リソース不足なく動作が確認できました。
まとめ
S3上に出力されたログリネーム処理、BluePrintを雛形として簡単に実装する事ができました。
CloudFrontのログ解析方法については、以下のブログで紹介させて頂いていますが、 ログ保持期間が長く、対象となるファイル数が多い場合、日付情報による絞込が作業効率の向上に効果的です。
今回の利用したLambdaファンクション、1ヶ月の稼働させた環境の稼働実績は、リクエスト数は7000強、コンピューティング時間は約450GB/秒でした。 Lambdaには、1,000,000 件のリクエスト、および 400,000 GB/秒のコンピューティング時間の無料枠がありますが、この枠内での利用が可能でした。
また、CloudFrontにかぎらず、S3のアクセスログなども同様に処理することが可能です。 ただし、S3に出力されるファイル数が極端に多い(毎秒100件超)場合、 Lambdaの同時実行数や、S3のキーがかたよる事によるパフォーマンス影響にもご注意ください。